fix(auth): seed/setup 비밀번호 해싱을 argon2id 로 통일 — Workers PBKDF2 상한 회피 - #72
Merged
Conversation
프로덕션 로그인이 hang 수정 후 다음 에러로 이어짐: NotSupportedError: Pbkdf2 failed: iteration counts above 100000 are not supported (requested 600000). 근본 원인: scripts/seed.ts 가 관리자 비밀번호를 PBKDF2 600,000회로 해싱했으나(pbkdf2$sha256:600000$...), Cloudflare Workers WebCrypto 는 PBKDF2 반복을 100,000 으로 제한한다. 따라서 시드된 관리자는 Workers 에서 verifyPbkdf2 의 deriveBits 가 throw 하여 로그인 자체가 불가능했다. setup.ts 는 100,000 을 써서 동작은 했으나 OWASP 미달 + seed 와 불일치. 수정: 앱의 정규 해시인 argon2id(@hicaru/argon2-pure.js — 순수 JS, Workers 호환)를 seed/setup 이 그대로 재사용하도록 변경. - scripts/seed.ts: hashPasswordPbkdf2 제거, src/lib/server/auth/password 의 hashPassword 재사용 - scripts/setup.ts: hashPasswordForSetup 제거, 동일하게 hashPassword 재사용 이로써 Workers 호환 + OWASP profile 5 즉시 적용 + PBKDF2 반복 상한 불일치 클래스 자체 제거 + 앱/스크립트 해시 경로 DRY. 주의: 이 수정은 이후 시드에만 적용된다. 이미 600k PBKDF2 로 시드된 프로덕션 관리자는 재시드/비밀번호 리셋이 필요하다. Co-Authored-By: Claude Opus 4.8 <[email protected]>
seed 재실행은 idempotent 라 기존 password credential 을 덮어쓰지 않는다. 따라서 과거 600k PBKDF2(Workers 검증 불가)로 시드된 관리자는 seed:pg 재실행으로는 복구되지 않는다. 이 전용 스크립트가 기존 사용자의 password credential secret 만 argon2id 로 교체(UPDATE)해 로그인을 복구한다. - scripts/reset-admin-password.ts: RESET_ADMIN_USERNAME|EMAIL + PASSWORD + (선택)TENANT_SLUG 를 env 로 받아(shell 히스토리/프로세스 목록 노출 회피) 대상 사용자의 password credential 을 hashPassword(argon2id)로 교체. 실행 후 verifyPassword 로 자체 검증(argon2id 형식 + 입력 비번 검증). 방언 무관(scripts/lib/db openScriptDb 재사용). - package.json: db:reset-admin[:pg|:mysql|:sqlite] 스크립트 추가. 검증(임시 sqlite DB, 프로덕션 무영향): 마이그레이션 → argon2id 시드 → 600k PBKDF2 로 강제 교체(깨진 관리자 재현) → 리셋 스크립트 → 교차검증(새 비번 valid=true, 옛/틀린 비번 valid=false). 전 단계 PASS. tsc(pg)/prettier/eslint clean. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
문제
PR #71(로그인 hang 수정)이 머지되어 hang 은 해소됐으나, 로그인이 비밀번호 검증 단계까지 도달하면서 다음 프로덕션 에러가 드러났습니다:
근본 원인
scripts/seed.ts가 관리자 비밀번호를 PBKDF2 600,000회로 해싱했으나(pbkdf2$sha256:600000$...), Cloudflare Workers WebCrypto 는 PBKDF2 반복을 100,000 으로 제한합니다. 따라서 시드된 관리자는 Workers 에서verifyPbkdf2의deriveBits가NotSupportedError를 던져 로그인 자체가 불가능했습니다. (scripts/setup.ts는 100,000 을 써서 동작은 했으나 OWASP 미달 + seed 와 불일치.)수정
1. seed/setup 해싱을 argon2id 로 통일
앱의 정규 해시인 argon2id(
@hicaru/argon2-pure.js— 순수 JS, Workers 호환)를 seed/setup 이 그대로 재사용하도록 변경했습니다.scripts/seed.tshashPasswordPbkdf2(600k) 제거 →src/lib/server/auth/password의hashPassword(argon2id) 재사용scripts/setup.tshashPasswordForSetup(100k PBKDF2) 제거 → 동일하게hashPassword재사용효과: Workers 호환 + OWASP profile 5 즉시 적용 + PBKDF2 반복 상한 불일치 클래스 자체 제거 + 앱/스크립트 해시 경로 DRY.
2. 관리자 비밀번호 리셋 스크립트 추가
seed:pg재실행은 idempotent 라 기존 password credential 을 덮어쓰지 않습니다. 따라서 위 수정만으로는 이미 600k PBKDF2 로 시드된 프로덕션 관리자가 복구되지 않습니다. 이를 위한 전용 스크립트를 추가했습니다.scripts/reset-admin-password.tssecret만hashPassword(argon2id)로 교체(UPDATE). 실행 후verifyPassword로 자체 검증. 방언 무관(scripts/lib/db재사용)package.jsondb:reset-admin[:pg|:mysql|:sqlite]스크립트 추가비밀번호는 shell 히스토리/프로세스 목록 노출을 피해 env 로 전달합니다:
DB_DIALECT=postgres \ RESET_ADMIN_USERNAME=admin \ RESET_ADMIN_PASSWORD='새-강력한-비밀번호' \ bun run db:reset-admin:pg검증
valid=true, 옛 비번/틀린 비번valid=false, 저장 해시$argon2id$.... 전 단계 PASShashPassword스모크 — 스크립트 컨텍스트 import 성공,$argon2id$v=19$m=7168...확인DB_DIALECT=postgres tsc --noEmit— cleanDB_DIALECT=postgres bun run build— clean (adapter-cloudflare)prettier --check+eslint— cleanDB_DIALECT=postgres RESET_ADMIN_USERNAME=<관리자> RESET_ADMIN_PASSWORD='<새 비번>' bun run db:reset-admin:pg(권장)IDP_BOOTSTRAP_ADMIN_*설정하여db:seed:pg재실행프로젝트 규칙상 원격 DB 적용 명령은 자동 실행하지 않으므로, 실제 리셋은 운영자가 값 확인 후 직접 실행해 주세요.
🤖 Generated with Claude Code